﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BFlatCompile.Nodes
{
	public class Variable : Expression
	{
		private static readonly ValueTable table = new ValueTable();

		public static ValueTable Table { get { return table; } }

		public string Name { get; private set; }
		public int Index { get { return table.GetId(this.Name); } }

		public object StaticReference { get; private set; }
		public bool IsThis { get; private set; }

		public Token Token { get; private set; }

		public Variable(string name, Token token)
		{
			this.StaticReference = null;
			this.IsThis = false;
			this.Name = name;
			table.Register(name);
			this.Token = token;
		}

		// This could either be a variable in the local scope or it could be static reference.
		// If it's a static reference, it's the root of a reference and must appear in 
		// class.AvailableReferences. 
		// If it's a not an available reference, but a member, it should throw an exception
		// since naming a variable that hides a class member (direct or base class) is not allowed.
		// Unless of course the member is a nested class. Then it's fine. 
		// Member resolution is resolved first before available references.

		public override void ResolveCode(Class c, Dictionary<object, object> parents)
		{
			object member = c.GetMember(this.Name, true);
			if (member != null)
			{
				if (member is Class)
				{
					this.StaticReference = member;
					return;
				}

				bool isStatic =
						(member is Field && ((Field)member).IsStatic) ||
						(member is Property && ((Property)member).IsStatic) ||
						(member is Method && ((Method)member).IsStatic);

				// TODO: check to see if any variables are used with the same name in the same scope?
				// this is a pretty annoying false positive error .
				/*
				if (isStatic)
				{
					throw new Exception("Must use class name to refer to static members.");
				}

				throw new Exception("Must use \"this\" to refer to class instance members.");
				*/
			}


			object reference;
			if (c.AvailableReferences.TryGetValue(this.Name, out reference))
			{
				if (reference is Class)
				{
					this.StaticReference = reference;
				}
				else if (reference is Namespace)
				{
					this.StaticReference = reference;
				}
				else
				{
					//throw new Exception("This shouldn't happen, should it?");
					// it can now because of the exceptions commented out above.
					// just leave it be and for now, trust the user is using a variable locally with this name.
				}
			}
		}

		public override void ResolveClassToThis(Class c)
		{
			if (this.StaticReference == c)
			{
				this.IsThis = true;
			}
		}
	}
}
